#include <iostream>
#include <vector>
#include <unordered_map>
#include <cstdint>
#include <functional>
#include <memory>
#include <unistd.h>

//ReleaseFunc: 这个是当对象没了，就意味着最后一个shared_ptr没了，那么就把对象从_timers清出去，这就是_release必须要执行的工作
//TaskFunc: 这个才是真正的业务工作，就是当超时时，要执行什么工作，比如：超时时，把连接断开或者只是打印一句你超时了，但不做任何处理
//           同时这个业务工作还可以被取消，就是说超时了，不做任何处理
//实质：TimerTask其实就是承担TaskFunc的处理工作，例如啥时候处理（延时时间），ID（为了使类在轮中操作更顺利），_canceled（看任务有没有被取消）
//      一切为业务工作服务，业务工作是里面的芯，TaskFunc是外面的壳
//    而ReleaseFunc只是：到时间了，这个业务任务已经完成或者已经被取消，TimerTask这个壳可以丢掉了，TimerTask也没必要在轮中留记录了，直接删除记录

using TaskFunc = std::function<void()>;   //业务任务
using ReleaseFunc = std::function<void()>;   //就是删除记录表中的记录的任务
class TimerTask{
    private:
        uint64_t _id;       // 定时器任务对象ID
        uint32_t _timeout;  //定时任务的超时时间
        bool _canceled;     // false-表示没有被取消， true-表示被取消（指业务任务）

        TaskFunc _task_cb;  //业务任务
        ReleaseFunc _release; //就是删除记录表中的记录的任务
    public:
        TimerTask(uint64_t id, uint32_t delay, const TaskFunc &cb): 
            _id(id), _timeout(delay), _task_cb(cb), _canceled(false) {}

        ~TimerTask() { 
            if (_canceled == false) _task_cb();    //没取消，执行业务任务
            _release();   
            //最后，不管有没有执行这个业务任务，都要删除记录，因为超时后，不管有没有执行这个业务任务，都要收尾结束了，这就是时间轮的游戏规则
        }
        void Cancel() { _canceled = true; }
        void SetRelease(const ReleaseFunc &cb) { _release = cb; }  
        uint32_t DelayTime() { return _timeout; }
};


//-------------------------------
//时间轮：秒针，容量，轮子，记录表
//1. 往轮子里面添加任务（添加任务本质就是添加TimerTask类的shared_ptr指针，已经写入记录表）
//2. 轮子里的任务的延迟（新增一个shared_ptr指针，在新的格子）
//3. 轮子里的任务的取消（让对应的TimerTask类取消执行业务函数）

//关键知识点：shared_ptr指针可以直接当类指针来用，可以直接->类函数，
//而weak_ptr不行，weak_ptr必须先转化得到一个shared_ptr指针，然后使用shared_ptr指针

//新增：任务ID—>直接转化为一个类——>直接添加就行
//刷新延迟/取消：任务ID——>查记录表——>weak_ptr——>shared_ptr——>添加新的shared_ptr指针/shared_ptr->取消函数
//为啥刷新不需要看我们的任务的pos地点，因为一旦刷新，任务的地点就依然确定，就是（指针位置+任务自身延迟时间）% 时间轮容量，不用看以前的旧的

//谈谈shared_ptr和weak_ptr：
//我们首先生成一个对象的时候，就会添加进入记录表，就会得到该对象的weak_ptr，然而weak_ptr可以生成无数个shared_ptr
//我们刷新的时候，不用把对象对应的旧shared_ptr在轮中去掉，只需要添加一个新的shared_ptr就行。
//然而我们的任务被秒钟追上时候，我们就把weak_ptr从记录表中删掉就行，不需要去管shared_ptr
//有可能存在weak_ptr删除，轮中对应的shared_ptr还存在的情况，就是我们下面讲的30s,5s的情况，但是我们秒钟到达一个格子，就会情况shared_ptr
//这时候shared_ptr的优点展示出来了，最后一个shared_ptr删除，任务对象自动删除，所以我们删除weak_ptr的时候，不用管任务对象的删除，shared_ptr会管

class TimerWheel {
    private:
        using WeakTask = std::weak_ptr<TimerTask>;
        using PtrTask = std::shared_ptr<TimerTask>;

        int _tick;      //当前的秒针，走到哪里释放哪里，释放哪里，就相当于执行哪里的任务
        int _capacity;  //表盘最大数量---其实就是最大延迟时间
        std::vector<std::vector<PtrTask>> _wheel;    //轮子

        //记录表
        std::unordered_map<uint64_t, WeakTask> _timers;  //<id , TimerTask对象指针>

    private:
        //通过ID来删除记录表中的内容
        void RemoveTimer(uint64_t id) {
            auto it = _timers.find(id);
            if (it != _timers.end()) {
                _timers.erase(it);
            }
        }

    public:
        TimerWheel():_capacity(60), _tick(0), _wheel(_capacity) {}

        //对象首次进入轮子
        //思路：通过TaskFunc构造了任务对象，将该对象的shared_ptr放入wheels中，
        //      将该对象的weak_ptr放入_timers中
        void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cb) {
            PtrTask pt(new TimerTask(id, delay, cb));
            pt->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
            int pos = (_tick + delay) % _capacity;
            _wheel[pos].push_back(pt);
            _timers[id] = WeakTask(pt);
        }

        //刷新/延迟定时任务
        //其实就是在新的格子里面加入对象的shared_ptr，而不是去删
        //思路：在记录表中通过id，找到该对象的weak_ptr，weak_ptr造一个shared_ptr，
        //      按照对象自己的任务延迟时间，选择格子，最后把shared_ptr放入格子中
        void TimerRefresh(uint64_t id) {
            auto it = _timers.find(id);
            if (it == _timers.end()) {
                return;//没找着定时任务，没法刷新，没法延迟
            }
            PtrTask pt = it->second.lock();//lock获取weak_ptr管理的对象对应的shared_ptr
            int delay = pt->DelayTime();
            int pos = (_tick + delay) % _capacity;   //好聪明，注意是：_tick + delay 而不是 该任务的pos + delay
            //保证了刷新后，每次的延迟时间一定最多是delay，超过delay，就被执行。
            //哪怕你最开始是在30s处，你被刷新了，并且你的DelayTime是5s，然而此时秒针的时间在3s，那么你马上就会变到8s处，变得更近
            _wheel[pos].push_back(pt);
        }

        //取消任务
        void TimerCancel(uint64_t id) {
            auto it = _timers.find(id);
            if (it == _timers.end()) {
                return;//没找着定时任务，没法刷新，没法延迟
            }
            PtrTask pt = it->second.lock();
            if (pt) pt->Cancel();
        }
        
        //时间轮的移动处理函数
        //提示：这个函数应该每秒钟被执行一次，相当于秒针向后走了一步
        //思路：把对应_tick位置的shared_ptr的数组清空即可
        void RunTimerTask() {
            _tick = (_tick + 1) % _capacity;
            _wheel[_tick].clear();  //clear使vector.size()==0;
        }
};

class Test {
    public:
        Test() {std::cout << "构造" << std::endl;}
        ~Test() {std::cout << "析构" << std::endl;}
};

void DelTest(Test *t) {
    delete t;
}

int main()
{
    TimerWheel tw;

    Test *t = new Test();

    tw.TimerAdd(888, 5, std::bind(DelTest, t));

    for(int i = 0; i < 5; i++) {
        sleep(1);
        tw.TimerRefresh(888);//刷新定时任务
        tw.RunTimerTask();//向后移动秒针
        std::cout << "刷新了一下定时任务，重新需要5s中后才会销毁\n";
    }
    tw.TimerCancel(888);
    while(1) {
        sleep(1);
        std::cout << "-------------------\n";
        tw.RunTimerTask();//向后移动秒针
    }
    return 0;
}

